﻿using System;
using Cpuss.ReportGenerator;
using NUnit.Framework;

namespace Cpuss.Console.Tests
{

    /// <summary>
    /// Tests for FlagParser.
    /// </summary>
    [TestFixture]
    public class FlagParserTest
    {

        /// <summary>
        /// Check to see that the input stream is correct.
        /// </summary>
        [Test]
        public void InputStreamTest()
        {
            string[] input = { "/repeat:10", "/small:12" };
            string[] expected = { "/repeat:10", "/small:12" };
            FlagParser actual = new FlagParser(input);

            CollectionAssert.AreEqual(expected, actual.GetInputStream());
        }

        /// <summary>
        /// Check to see that the correct exception is thrown if the input stream is
        /// null.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void InputStreamNullTest()
        {
            string[] input = null;
            FlagParser actual = new FlagParser(input);
        }

        /// <summary>
        /// Check to see that the repeat value is correct.
        /// </summary>
        [Test]
        public void RepeatTest()
        {
            string[] input = { "/repeat:12", "/small:10" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();

            Assert.AreEqual(12, actual.Repeat);
        }

        /// <summary>
        /// Check to see that the strategy is correct.
        /// </summary>
        [Test]
        public void Strategyest()
        {
            string[] input = { "/repeat:33", "/strategy:FCFS" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();

            Assert.AreEqual("FCFS", actual.Strategy);
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when the value of repeat is not an int.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentException))]
        public void RepeatValueNotIntTest()
        {
            string[] input = { "/repeat:betty", "/strategy:FCFS" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();
        }

        /// <summary>
        /// Check to see that the message of the thrown exception is as expected.
        /// </summary>
        [Test]
        public void RepeatValueNotIntErrorMessageTest()
        {
            string[] input = { "/repeat:betty", "/strategy:FCFS" };
            FlagParser parser = new FlagParser(input);
            string actual = string.Empty;

            try
            {
                parser.Parse();
            }
            catch (ArgumentException ex)
            {
                actual = ex.Message;
            }

            Assert.AreEqual("The value of flag: /repeat should be an integer.", actual);
        }

        /// <summary>
        /// Check to see that the correct exception is raised when a flag has no value.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentException))]
        public void FlagHasNoValueTest()
        {
            string[] input = { "/repeat:10", "/strategy:" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();
        }

        /// <summary>
        /// Check to see that the error message of the thrown exception is as expected.
        /// </summary>
        [Test]
        public void FlagHasNoValueErrorMessageTest()
        {
            string[] input = { "/repeat:10", "/strategy:" };
            FlagParser parser = new FlagParser(input);
            string actual = string.Empty;

            try
            {
                parser.Parse();
            }
            catch (ArgumentException ex)
            {
                actual = ex.Message;
            }

            Assert.AreEqual("The flag: /strategy has no associated value.", actual);
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when a flag is not in the correct format.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentException))]
        public void FlagNotCorrectFormatTest()
        {
            string[] input = { "/repeat:10", "strategy:" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();
        }

        /// <summary>
        /// Check to see that the error message from the thrown exception is as expected.
        /// </summary>
        [Test]
        public void FlagNotCorrectFormatErrorMessageTest()
        {
            string[] input = { "/repeat10", "/strategy:FCFS" };
            FlagParser parser = new FlagParser(input);
            string actual = string.Empty;

            try
            {
                parser.Parse();
            }
            catch (ArgumentException ex)
            {
                actual = ex.Message;
            }

            Assert.AreEqual("The flag: /repeat10 is in an incorrect format", actual);
        }

        /// <summary>
        /// Check to see that the name of the file specified is correct.
        /// </summary>
        [Test]
        public void ReportTest()
        {
            string[] input = { "/strategy:FCFS", "/repeat:3", "/report:Test" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();

            Assert.AreEqual("Test", actual.Report);
        }

        /// <summary>
        /// Check to see that the number of small processes is correct.
        /// </summary>
        [Test]
        public void SmallTest()
        {
            string[] input = { "/report:Test", "/small:10" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();

            Assert.AreEqual(10, actual.Small);
        }

        /// <summary>
        /// Check to see that the number of medium processes is correct.
        /// </summary>
        [Test]
        public void MediumTest()
        {
            string[] input = { "/small:1", "/report:Test", "/medium:12" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();

            Assert.AreEqual(12, actual.Medium);
        }

        /// <summary>
        /// Check to see that the number of medium processes is correct.
        /// </summary>
        [Test]
        public void LargeTest()
        {
            string[] input = { "/report:Betty", "/medium:1", "/large:3" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();

            Assert.AreEqual(3, actual.Large);
        }

        /// <summary>
        /// Check to see that the output dir is correct.
        /// </summary>
        [Test]
        public void OutdirTest()
        {
            string[] input = { "/small:0", "/strategy:SJF", @"/outdir:c:\mydocs" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();

            Assert.AreEqual(@"c:\mydocs", actual.Outdir);
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when the flag is not known.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentException))]
        public void UnknownFlagTest()
        {
            string[] input = { "/repeat:10", "/generate:abc" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();
        }

        /// <summary>
        /// Check to see that the error message is as expected.
        /// </summary>
        [Test]
        public void UnknownFlagErrorMessageTest()
        {
            string[] input = { "/repeat:10", "/generate:abc" };
            FlagParser parser = new FlagParser(input);
            string actual = string.Empty;

            try
            {
                parser.Parse();
            }
            catch (ArgumentException ex)
            {
                actual = ex.Message;
            }

            Assert.AreEqual("The flag: /generate is not recognised.", actual);
        }

        /// <summary>
        /// Check to see that an uppercase flag is acceptable.
        /// </summary>
        [Test]
        public void UpperCaseFlagTest()
        {
            string[] input = { "/REPEAT:10", "/strategy:FCFS" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();

            Assert.AreEqual(10, actual.Repeat);
        }

        /// <summary>
        /// Check to see that the constructor args returned are correct.
        /// </summary>
        [Test]
        public void ConstructorNoArgsTest()
        {
            string[] input = { "/repeat:12", "/strategy:FirstComeFirstServed", "/report:Test" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();

            Assert.AreEqual(0, actual.ConstructorArguments.Count);
            Assert.AreEqual("FirstComeFirstServed", actual.Strategy);
        }

        /// <summary>
        /// Check to see that the constructor args are correct.
        /// </summary>
        [Test]
        public void ConstructorSingleArgTest()
        {
            string[] input = { "/strategy:SJF,1", "/repeat:12", "/small:10" };
            FlagParser actual = new FlagParser(input);
            object[] expected = { 1 };

            actual.Parse();

            CollectionAssert.AreEqual(expected, actual.ConstructorArguments);
            Assert.AreEqual("SJF", actual.Strategy);
        }

        /// <summary>
        /// Check to see that the args are correct.
        /// </summary>
        [Test]
        public void ConstructorTwoArgsTest()
        {
            string[] input = { "/strategy:SJFE,6, 27", "/repeat:10" };
            FlagParser actual = new FlagParser(input);
            object[] expected = { 6, 27 };

            actual.Parse();

            CollectionAssert.AreEqual(expected, actual.ConstructorArguments);
        }

        /// <summary>
        /// Check to see that the args are correct.
        /// </summary>
        [Test]
        public void ConstructorManyArgsTest()
        {
            string[] input = { "/repeat:10", "/small:23", "/strategy:SJFE,23,12,2,3,4,12" };
            FlagParser actual = new FlagParser(input);
            object[] expected = { 23, 12, 2, 3, 4, 12 };

            actual.Parse();

            CollectionAssert.AreEqual(expected, actual.ConstructorArguments);
        }

        /// <summary>
        /// All strategies if they accept parameters will be of basic int32 type, thus if it isn'args
        /// then we have an erroneous param value.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentException))]
        public void ConstructorTypeNotAcceptedTest()
        {
            string[] input = { "/repeat:10", "/strategy:FCFS,12,fes" };
            FlagParser actual = new FlagParser(input);

            actual.Parse();
        }

        /// <summary>
        /// Check to see that the error message is as expected.
        /// </summary>
        [Test]
        public void ConstructorTypeNotAccpetedErrorMessageTest()
        {
            string[] input = { "/repeat:10", "/strategy:FCFS,12,fes" };
            string actual = string.Empty;
            FlagParser parser = new FlagParser(input);

            try
            {
                parser.Parse();
            }
            catch (ArgumentException ex)
            {
                actual = ex.Message;
            }

            Assert.AreEqual("One of the parameters for the strategy specified is not of type Int32.", actual);
        }

        /// <summary>
        /// Check to see that the strategy is created correctly.
        /// </summary>
        [Test]
        public void ConstructorNoArgsInvocationTest()
        {
            string[] input = { "/strategy:FirstComeFirstServed" };
            FlagParser parser = new FlagParser(input);

            parser.Parse();
            object[] args = new object[parser.ConstructorArguments.Count];
            for (int i = 0; i < args.Length; i++)
            {
                args[i] = parser.ConstructorArguments[i];
            }
            StrategyFactory factory = new StrategyFactory(parser.Strategy, args);

            Assert.IsNotNull(factory.CreateInstance());
        }

        /// <summary>
        /// Check to see that the strategy is created correctly.
        /// </summary>
        [Test]
        public void ConstructorMultipleArgsInvocationTest()
        {
            string[] input = { "/repeat:12", "/small:2", "/strategy:ShortestJobFirstExpert,10,45" };
            FlagParser parser = new FlagParser(input);

            parser.Parse();
            object[] args = new object[parser.ConstructorArguments.Count];
            for (int i = 0; i < args.Length; i++)
            {
                args[i] = parser.ConstructorArguments[i];
            }

            StrategyFactory factory = new StrategyFactory(parser.Strategy, args);

            Assert.IsNotNull(factory.CreateInstance());
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void WindowTest()
        {
            string[] input = {"/window:12", "/small:3"};
            FlagParser actual = new FlagParser(input);

            actual.Parse();

            Assert.AreEqual(12, actual.Window);
        }
    }

}
